package gochat

import (
	"crypto/md5"
	"encoding/hex"
	"encoding/json"
	"golang.org/x/net/websocket"
	"log"
	"sync"
)

type WS struct {
	conn *sync.Map
	msg  []Msg
}

var myWs *WS
var once sync.Once

func NewWS() *WS {
	once.Do(func() {
		myWs = new(WS)
		myWs.conn = new(sync.Map)
		myWs.msg = []Msg{}
	})
	return myWs
}

func WsHandler() websocket.Handler {
	var ws = NewWS()

	// 交给消息接管中心
	return ws.Publish
}

func (w *WS) authentication(ws *websocket.Conn) error {
	var r = ws.Request()
	log.Println(r.URL.Query())
}

func (w *WS) Publish(ws *websocket.Conn) {
	var err error

	// 认证
	if err = w.authentication(ws);err!=nil {
		panic(err.Error())
	}

	for {
		var reply string
		if err = websocket.Message.Receive(ws, &reply); err != nil {
			log.Println("无法接收消息,",err.Error())
			break
		}
		log.Println(reply)

		msg := NewMsg(reply)

		//w.msg <- msg
		if msg.Msg == "" {
			continue
		}
		w.msg = append(w.msg, msg)

		// 如果不存在, 则添加到用户组中, 并且把最近的10条消息发给他
		if !w.checkConnExists(msg.Username) {
			// 添加到用户组
			w.AddConn(msg.Username, ws)
			//// 把最近的10条消息发给他
			w.SendMsg(w.msg)
			//w.SendMsg([]Msg{msg})
		} else {
			// 分发消息
			w.SendMsg([]Msg{msg})
		}
	}
}

func (w *WS) SendMsg(msg []Msg) {
	w.conn.Range(func(key, value interface{}) bool {
		go func() {
			if err := websocket.Message.Send(value.(*websocket.Conn), packMsg(msg)); err != nil {
				log.Println("发送消息到客户端失败:", key)
			} else {
				log.Printf("发送消息到客户端(%s)成功\n", key)
			}
		}()
		return true
	})
}

func packMsg(msg []Msg) string {
	js, err := json.Marshal(msg)
	if err != nil {
		panic(err.Error())
	}
	return string(js)
}

func (w *WS) AddConn(username string, ws *websocket.Conn) {
	w.conn.Store(w.getConnToken(username), ws)
}

func (w *WS) checkConnExists(username string) bool {
	var key = w.getConnToken(username)
	if _, ok := w.conn.Load(key); ok {
		return true
	} else {
		return false
	}
}

func (w *WS) getConnToken(username string) string {
	//return MD5(username)
	return username
}
func MD5(str string) string {
	h := md5.New()
	h.Write([]byte(str))
	return hex.EncodeToString(h.Sum(nil))
}

type Msg struct {
	Msg      string `json:"msg"`
	Username string `json:"username"`
}

func NewMsg(arg string) Msg {
	var js Msg
	err := json.Unmarshal([]byte(arg), &js)
	if err != nil {
		panic(err.Error())
	}
	return js
}
